﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Text;
using IrishSettings.TableControllers;
using Wrappers.WebContext;
using Dataglot;

namespace IrishSettings
{

    //TODO: Actually add a caching feature
    public class IrishSettingsManagerCached : IIrishSettingsManager
    {
        private const string CacheName= "IrishSettings.Settings";
        private readonly ICacheWrapper cache;

        private IrishSettingsManager manager;
        private SettingsValues cachedSettings;
        private SettingsApplication currentApp;

        public IrishSettingsManagerCached(SettingsApplication app, ICacheWrapper newCache)
        {            
            if (newCache == null)
            {
                throw new ArgumentException("Cache can't be null. If you need a non-cached SettingsManager, use IrishSettingsManager");
            }
            app.ValidateRequirements();

            currentApp = app;
            cache = newCache;

            //Don't initialize a IrishSettingsManager here, since that requires a db hit.
            
        }

        private IrishSettingsManager Manager()
        {
            if(manager==null)
            {
                DataFactory factory = new DataFactory(
                            ConfigurationManager.AppSettings["Dataglot.ConnectionStringName"]
                        );

                //DbApplicationManager appManager = new DbApplicationManager();
                DbSchemaManager schemaManager = new DbSchemaManager(currentApp, factory);

                if (currentApp.Id == 0)
                {
                    
                    DbApplicationManager appManager = new DbApplicationManager(factory);
                    //Load or create application object
                    currentApp = appManager.CreateApplication(factory,currentApp, schemaManager);
                }

                if (!schemaManager.SchemaIsStored(currentApp,currentApp.VersionObject))
                {
                    schemaManager.InsertSchema(currentApp.Schema);
                }

                manager = new IrishSettingsManager(currentApp);

                //Can defer this for perf.
                InitializeCacheIfNotAlreadyDone();
                return manager;
            }
            else
            {
                return manager;
            }
            
        }

        public SettingsValues Initialize()
        {
            if (cachedSettings==null)
            {

                if (cache[CacheName] == null)
                {
                    //Retrieve from database and cache.
                    cachedSettings = Manager().Initialize();
                    cache[CacheName] = cachedSettings;
                    return (SettingsValues)cache[CacheName];
                }
                else
                {
                    //This persists until evicted from the ASP.NET cache 
                    //(or what ever cache is being used)
                    return (SettingsValues)cache[CacheName];
                }
            }
            else
            {
                //This persists for life of object.
                return cachedSettings;
            }
        }

        public void Set(string name, object value)
        {
            //This happens rarely, so we will value simplicity
            //over performance & not batch up the updates.
            Manager().Set(name, value);
            ResetCache();
        }

        private void ResetCache()
        {
            cache[CacheName] = null;
            cachedSettings = null;
            Initialize();
        }

        public void Save()
        {
            Manager().Save();
            ResetCache();
        }


        public GenericSetting Setting(string name)
        {
            InitializeCacheIfNotAlreadyDone();

            if (cachedSettings.Count==0)
            {
                throw new InvalidOperationException("Cached settins is empty, won't be able to return any results.");
            }
            try
            {
                return cachedSettings[name];
            }
            catch (KeyNotFoundException)
            {
                foreach (KeyValuePair<string, GenericSetting> cachedSetting in cachedSettings)
                {
                    Trace.WriteLine(cachedSetting.Key); 
                }
                throw new Exception("Can't find key " + name + " in schema");
            }
        }

        private void InitializeCacheIfNotAlreadyDone()
        {
            if (cachedSettings == null)
            {
                cachedSettings = Initialize();
            }
            if (cachedSettings==null)
                throw new InvalidOperationException("Retrieving settings failed, cachedSettings is still null");
            if (cachedSettings.Count == 0)
            {
                throw new InvalidOperationException("Cached settings is empty, won't be able to return any results.");
            }
        }

        public GenericSetting this[string name]
        {
            get
            {
                InitializeCacheIfNotAlreadyDone();

                try
                {
                    return cachedSettings[name];
                }
                catch(KeyNotFoundException)
                {
                    throw new Exception("Can't find key " + name + " in schema");
                }
            }
            set { Manager()[name] = value; }
        }

        public Version VersionPrevious()
        {
            return Manager().VersionPrevious();
        }

        public Version VersionCurrent()
        {
            return Manager().VersionCurrent();
        }

        public void Reset()
        {
            Manager().Reset();
            ResetCache();
        }

        //public void Reset(Version version)
        //{
        //    manager.Reset(version);
        //    ResetCache();
        //}

        //public void Reset(string application, Version version)
        //{
        //    manager.Reset(application,version);
        //    ResetCache();
        //}
    }
}
